﻿using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;

namespace Yeap.Kernel.Unity
{
    /// <summary>
    /// http://buksbaum.us/2009/02/08/type-tracking-extension-for-unity/
    /// </summary>
    class TypeTrackingExtension : UnityContainerExtension
    {
        #region Fields
        private readonly Dictionary<Type, HashSet<string>> _registeredTypes = new Dictionary<Type, HashSet<string>>();
        #endregion

        #region Private Methods
        protected override void Initialize()
        {
            Context.RegisteringInstance += OnNewInstance;
            Context.Registering += OnNewType;
        }
        private void OnNewInstance(object sender, RegisterInstanceEventArgs e)
        {
            HashSet<string> names;
            string name = string.IsNullOrEmpty(e.Name) ? string.Empty : e.Name;

            if (!_registeredTypes.TryGetValue(e.RegisteredType, out names))
            { //  not found, so add it
                _registeredTypes.Add(e.RegisteredType, new HashSet<string> { name });
            }
            else
            { //  already added type, so add name
                names.Add(name);
            }
        }
        private void OnNewType(object sender, RegisterEventArgs e)
        {
            HashSet<string> names;
            string name = string.IsNullOrEmpty(e.Name) ? string.Empty : e.Name;
            if (!_registeredTypes.TryGetValue(e.TypeFrom, out names))
            { //  not found, so add it
                _registeredTypes.Add(e.TypeFrom, new HashSet<string> { name });
            }
            else
            { //  already added type, so add name
                names.Add(name);
            }
        }
        #endregion

        #region CanResolve

        public bool CanResolve<T>()
        {
            return CanResolve<T>(null);
        }

        public bool CanResolve<T>(string name)
        {
            HashSet<string> names;
            if (_registeredTypes.TryGetValue(typeof(T), out names))
            {
                return names.Contains(name ?? string.Empty);
            }
            return false;
        }

        public bool CanResolveAny<T>()
        {
            return _registeredTypes.ContainsKey(typeof(T));
        }
        #endregion

        #region TryResolve

        public T TryResolve<T>()
        {
            return TryResolve<T>(default(T));
        }

        public T TryResolve<T>(string name)
        {
            return TryResolve<T>(name, default(T));
        }

        public T TryResolve<T>(T defaultValue)
        {
            if (!CanResolve<T>())
                return defaultValue;
            return Container.Resolve<T>();
        }

        public T TryResolve<T>(string name, T defaultValue)
        {
            if (!CanResolve<T>(name))
                return defaultValue;
            return Container.Resolve<T>(name);
        }
        #endregion
    }
}